package task

import (
	"context"
	"google.golang.org/protobuf/types/known/timestamppb"
	"gorm.io/gorm"
	"strings"
	"time"
	"wx-dailydoer/pb"
)

const (
	Monday    = 0b0000001
	Tuesday   = 0b0000010
	Wednesday = 0b0000100
	Thursday  = 0b0001000
	Friday    = 0b010000
	Saturday  = 0b100000
	Sunday    = 0b1000000
)

var (
	weekdays    = []int64{Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}
	weekdaysMap = map[int64]int64{
		Monday:    1,
		Tuesday:   2,
		Wednesday: 3,
		Thursday:  4,
		Friday:    5,
		Saturday:  6,
		Sunday:    7,
	}
)

type Task struct {
	gorm.Model
	Name       string `gorm:"column:name;type:VARCHAR(255);"`
	Desc       string `gorm:"column:desc;type:VARCHAR(255);"`
	Freq       int64
	Credit     int64
	StartedAt  time.Time
	EndedAt    time.Time
	DaysInWeek int64 // Binary
	DayInMonth int64
	Tags       string
	IsEnabled  bool
	CreatedBy  string `gorm:"column:created_by;type:VARCHAR(40);"`
}

func (table *Task) ConvertToDTO() *pb.Task {
	return &pb.Task{
		Id:         int64(table.ID),
		Name:       table.Name,
		Desc:       table.Desc,
		Freq:       table.Freq,
		StartedAt:  timestamppb.New(table.StartedAt),
		EndedAt:    timestamppb.New(table.EndedAt),
		DaysInWeek: bin2SliceWeekdays(table.DaysInWeek),
		DayInMonth: table.DayInMonth,
		Tags:       extractTags(table.Tags),
		IsEnabled:  table.IsEnabled,
	}
}

func extractTags(tags string) []string {
	return strings.Split(tags, ",")
}

func bin2SliceWeekdays(binWeekdays int64) []int64 {
	wds := []int64{}
	for _, day := range weekdays {
		if binWeekdays&day != 0 {
			wds = append(wds, weekdaysMap[day])
		}
	}
	return wds
}

func (table *Task) TableName() string {
	return "task"
}

type TaskDAO struct {
	db *gorm.DB
}

func NewTaskDAO(db *gorm.DB) *TaskDAO {
	return &TaskDAO{
		db: db,
	}
}

func (dao *TaskDAO) Create(ctx context.Context, task *Task) error {
	return dao.db.WithContext(ctx).Create(task).Error
}

func (dao *TaskDAO) Update(ctx context.Context, task *Task) error {
	return dao.db.WithContext(ctx).Save(task).Error
}

func (dao *TaskDAO) Delete(ctx context.Context, task *Task) error {
	return dao.db.WithContext(ctx).Delete(task).Error
}

func (dao *TaskDAO) GetById(ctx context.Context, id int64) (*Task, error) {
	var collection Task
	err := dao.db.WithContext(ctx).First(&collection, id).Error
	if err != nil {
		return nil, err
	}
	return &collection, nil
}

func (dao *TaskDAO) GetAll(ctx context.Context) ([]*Task, error) {
	var collections []*Task
	err := dao.db.WithContext(ctx).Find(&collections).Error
	if err != nil {
		return nil, err
	}
	return collections, nil
}
